#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector< vector<int> > g; // Задает дерево
vector<bool> used; // Массив для поиска корня в дереве. 
long long tree_sum = 0; // Ответ на задачу
vector<long long> sum, a; // sum - сумма чисел в поддереве, a - массив ключей

void init(int n)
{
	a.resize(n, 0);
	sum.resize(n, 0);
	for (int i = 0; i < n; i++)
		a[i] = rand(); // Рандомно заполняем значения ключей
}

int dfs(int v)
{
	for (size_t i = 0; i< g[v].size(); i++)
	{
		int u = g[v][i];
		sum[v] += dfs(u); // Суммирует сыновей
	}
	sum[v] += a[v];
	tree_sum += a[v]; // Увеличивает текущею сумму
	return sum[v];
}

int main()
{
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	int n;
	cin >> n; // Читаем количество вершин в дереве
	int root = -1;
	g.resize(n);
	used.resize(n, false);
	for (size_t i = 0; i < n; i++)
	{
		int m;
		cin >> m;
		g[i].resize(m); // Количество вычеслителей (количество потомков)
		for (size_t j = 0; j < m; j++)
		{
			cin >> g[i][j]; // Номера вершин потомков. Вершины номеруются с 0
			used[g[i][j]] = true; // Говорим, что у g[i][j] вершины, есть предок, следовательно она не может быть корнем
		}
	}
	for (size_t i = 0; i < n; i++)
		if (!used[i]) root = i; // Находим корень
	used.assign(n, false);
	init(n); // Инициалицируем значения ключей
	for (size_t i = 0; i < n; i++)
		cout << a[i] << ((i== n - 1) ? "\n" : " ");
	dfs(root); // Считаем ответ на задачу
	cout << tree_sum << "\n"; // Совпадает с sum[root]
}